package com.api.gateway.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Consumer;

/**
 * @author BruceLee
 */
@Slf4j
@Component
public class JwtFilter implements GlobalFilter, Ordered {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Value("${JwtFilter.enable}")
    private boolean enable;

    static List<String> endWithFilterList = new ArrayList<>();
    static List<String> includeFilterList = new ArrayList<>();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //判断开关
        if (!enable) {
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        log.info("Start Filter > " + request.getPath().toString());
        for (String path : includeFilterList) {
            if (request.getPath().toString().contains(path)) {
                return chain.filter(exchange);
            }
        }

        for (String path : endWithFilterList) {
            if (request.getPath().toString().endsWith(path)) {
                return chain.filter(exchange);
            }
        }

        String loginInfo = parse(request);
        if (loginInfo == null) {
            log.info("鉴权失败 ----");
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        // 想要添加更多的header写在这儿就可以了
        Consumer<HttpHeaders> httpHeaders = httpHeader -> {
            //请求头转码
            String source = URLEncoder.encode(loginInfo, StandardCharsets.UTF_8);
            httpHeader.set("login-info", source);
        };

        request.mutate().headers(httpHeaders).build();
        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public int getOrder() {
        return 0;
    }

    public String parse(ServerHttpRequest request) {
        String campToken = request.getHeaders().getFirst("campToken");
        if (campToken == null || "".equals(campToken)) {
            log.warn("未查找到Token,鉴权未通过");
            return null;
        }
        String menuId = request.getHeaders().getFirst("menuId");
        // 获取基础信息
        if ("123456".equals(campToken)) {
            Map<String, Object> parse = new HashMap<>(5);
            parse.put("name", "超级管理员");
            parse.put("personNo", "10000001");
            parse.put("userId", "4ba33cf85be349d894ef72f8ca4fc9cc");
            parse.put("menuId", menuId);
            parse.put("departmentNoPower", this.getDepartmentPower(menuId == null ? "6c40a7c7f87a4f9f8af9470043e484a5" : menuId, "10000001"));
            log.info(parse.toString());
            return JSON.toJSONString(parse);
        }
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey("speak").parseClaimsJws(campToken).getBody();
        } catch (ExpiredJwtException | MalformedJwtException e) {
            log.warn("token 已超时 : {}", e.getMessage());
            return null;
        }
        Map<String, Object> parse = (Map<String, Object>) claims.get("loginInfo");
        String token = (String) stringRedisTemplate.opsForHash().get("login_user", parse.get("userId"));
        if (!campToken.equals(token)) {
            log.warn("登陆已失效");
            return null;
        }
        parse.put("menuId", menuId);
        parse.put("departmentNoPower", this.getDepartmentPower(menuId, (String) parse.get("personNo")));
        log.info("当前登陆信息 > {},Token鉴定位 > {}", parse.toString() , token.substring(campToken.length() - 10));
        return JSON.toJSONString(parse);
    }

    public List<String> getDepartmentPower(String menuId, String personNo) {
        List<String> list = new ArrayList<>();
        List<Map<String, Object>> departmentPowers = (List<Map<String, Object>>) JSONObject.parse((String) stringRedisTemplate.opsForHash().get("role_power", personNo));
        if (departmentPowers != null) {
            for (Map<String, Object> departmentPower : departmentPowers) {
                if (menuId.equals(departmentPower.get("id"))) {
                    list = (List<String>) departmentPower.get("departmentNos");
                    break;
                }
            }
        }
        return list;
    }

    static {
        endWithFilterList.add("/flush/token");
        endWithFilterList.add("/login");
        endWithFilterList.add("/register/menu");
        endWithFilterList.add("/register/dict");

        // 数据平台放行
        includeFilterList.add("/data-platform/hasorweb/web/");
        includeFilterList.add("/data-platform/openapi/v1");

        //lic校验放行
        includeFilterList.add("/verification/install");
        includeFilterList.add("/verification/system/info/str");
        includeFilterList.add("/verification/system/info");

        // swagger
        includeFilterList.add("/api-docs-ext");
        includeFilterList.add("/pg");
        includeFilterList.add("/api-docs");
        includeFilterList.add(".html");
        includeFilterList.add(".js");
        includeFilterList.add(".css");
        includeFilterList.add(".zz");
        includeFilterList.add(".exe");
        includeFilterList.add(".ico");
    }

}
